/******************************************************************************
 * Copyright (C) 2015  671643387@qq.com
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *****************************************************************************/

#ifndef SHARED_UTIL_H_
#define SHARED_UTIL_H_

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif

#include <cstddef>
#include <cstdint>
#include <string>
#include <NodeCpp/String.h>
using NodeCpp::Tokens;

inline bool isBasicLatinCharacter(wchar_t _Wchar)
{
    if (_Wchar >= L'a' && _Wchar <= L'z') {
        return true;
    }
    if (_Wchar >= L'A' && _Wchar <= L'Z')  {
        return true;
    }
    return false;
}

inline bool isExtendedLatinCharacter(wchar_t _Wchar)
{
    if (isBasicLatinCharacter(_Wchar)) {
        return true;
    }
    if (_Wchar >= 0x00C0 && _Wchar <= 0x00D6) {
        return true;
    }
    if (_Wchar >= 0x00D8 && _Wchar <= 0x00DF) {
        return true;
    }
    if (_Wchar == 0x00DF) {
        return true;
    }
    if (_Wchar >= 0x00E0 && _Wchar <= 0x00F6) {
        return true;
    }
    if (_Wchar >= 0x00F8 && _Wchar <= 0x00FE) {
        return true;
    }
    if (_Wchar >= 0x0100 && _Wchar <= 0x012F) {
        return true;
    }
    if (_Wchar == 0x1E9E) {
        return true;
    }
    return false;
}

inline bool isCyrillicCharacter(wchar_t _Wchar)
{
    if (_Wchar >= 0x0410 && _Wchar <= 0x044F) {
        return true;
    }
    if (_Wchar == 0x0401 || _Wchar == 0x0451) {
        return true;
    }
    return false;
}

inline bool isEastAsianCharacter(wchar_t _Wchar)
{
    if (_Wchar >= 0x1100 && _Wchar <= 0x11F9) {
        return true;
    }
    if (_Wchar >= 0x3041 && _Wchar <= 0x30FF) {
        return true;
    }
    if (_Wchar >= 0x3131 && _Wchar <= 0x318E) {
        return true;
    }
    if (_Wchar >= 0x31F0 && _Wchar <= 0x31FF) {
        return true;
    }
    if (_Wchar >= 0x3400 && _Wchar <= 0x4DB5) {
        return true;
    }
    if (_Wchar >= 0x4E00 && _Wchar <= 0x9FC3) {
        return true;
    }
    if (_Wchar >= 0xAC00 && _Wchar <= 0xD7A3) {
        return true;
    }
    if (_Wchar >= 0xFF01 && _Wchar <= 0xFFEE) {
        return true;
    }
    return false;
}

inline bool isWhiteSpace(char _C) {
    return ::isspace(int(_C)) != 0;
}

inline bool isNumeric(wchar_t _Wchar) {
    return (_Wchar >= L'0' && _Wchar <= L'9');
}

inline bool isNumeric(char _C) {
    return (_C >= '0' && _C <= '9');
}

inline bool isNumericOrSpace(wchar_t _Wchar) {
    return isNumeric(_Wchar) || _Wchar == L' ';
}

inline bool isNumeric(char const* _Str)
{
    for (char const* _C = _Str; *_C; ++_C)
    if (!isNumeric(*_C)) {
        return false;
    }
    return true;
}

inline bool isNumeric(std::string const& _Str)
{
    for (std::string::const_iterator _Itr = _Str.begin(); 
        _Itr != _Str.end(); ++_Itr)
    if (!isNumeric(*_Itr)) {
        return false;
    }
    return true;
}

inline bool isNumeric(std::wstring const& _Str)
{
    for (std::wstring::const_iterator _Itr = _Str.begin(); 
        _Itr != _Str.end(); ++_Itr)
    if (!isNumeric(*_Itr)) {
        return false;
    }
    return true;
}

inline bool isBasicLatinString(std::wstring _Wstr, bool _NumericOrSpace)
{
    for (std::size_t _I = 0; _I < _Wstr.size(); ++_I)
    if (!isBasicLatinCharacter(_Wstr[_I]) 
        && (!_NumericOrSpace || !isNumericOrSpace(_Wstr[_I]))) {
        return false;
    }
    return true;
}

inline bool isExtendedLatinString(std::wstring _Wstr, bool _NumericOrSpace)
{
    for (std::size_t _I = 0; _I < _Wstr.size(); ++_I)
    if (!isExtendedLatinCharacter(_Wstr[_I])
        && (!_NumericOrSpace || !isNumericOrSpace(_Wstr[_I]))) {
        return false;
    }
    return true;
}

inline bool isCyrillicString(std::wstring _Wstr, bool _NumericOrSpace)
{
    for (std::size_t _I = 0; _I < _Wstr.size(); ++_I)
    if (!isCyrillicCharacter(_Wstr[_I]) 
        && (!_NumericOrSpace || !isNumericOrSpace(_Wstr[_I]))) {
        return false;
    }
    return true;
}

inline bool isEastAsianString(std::wstring _Wstr, bool _NumericOrSpace)
{
    for (std::size_t _I = 0; _I < _Wstr.size(); ++_I)
    if (!isEastAsianCharacter(_Wstr[_I])
        && (!_NumericOrSpace || !isNumericOrSpace(_Wstr[_I])))
    {
        return false;
    }
    return true;
}

inline wchar_t wcharToLower(wchar_t _Wchar)
{
    if (_Wchar >= L'A' && _Wchar <= L'Z') {
        return wchar_t(std::uint16_t(_Wchar) + 0x0020);
    }
    if (_Wchar >= 0x00C0 && _Wchar <= 0x00D6) {
        return wchar_t(std::uint16_t(_Wchar) + 0x0020);
    }
    if (_Wchar >= 0x00D8 && _Wchar <= 0x00DE) {
        return wchar_t(std::uint16_t(_Wchar) + 0x0020);
    }
    if (_Wchar >= 0x0100 && _Wchar <= 0x012E) {
        if (_Wchar % 2 == 0) {
            return wchar_t(std::uint16_t(_Wchar) + 0x0001);
        }
    }
    if (_Wchar == 0x1E9E) {
        return wchar_t(0x00DF);
    }
    if (_Wchar == 0x0401) {
        return wchar_t(0x0451);
    }
    if (_Wchar >= 0x0410 && _Wchar <= 0x042F) {
        return wchar_t(std::uint16_t(_Wchar) + 0x0020);
    }

    return _Wchar;
}

inline wchar_t wcharToUpper(wchar_t _Wchar)
{
    if (_Wchar >= L'a' && _Wchar <= L'z') {
        return wchar_t(std::uint16_t(_Wchar) - 0x0020);
    }
    if (_Wchar == 0x00DF) {
        return wchar_t(0x1E9E);
    }
    if (_Wchar >= 0x00E0 && _Wchar <= 0x00F6) {
        return wchar_t(std::uint16_t(_Wchar) - 0x0020);
    }
    if (_Wchar >= 0x00F8 && _Wchar <= 0x00FE) {
        return wchar_t(std::uint16_t(_Wchar) - 0x0020);
    }
    if (_Wchar >= 0x0101 && _Wchar <= 0x012F) {
        if (_Wchar % 2 == 1)
        {
            return wchar_t(std::uint16_t(_Wchar) - 0x0001);
        }
    }
    if (_Wchar >= 0x0430 && _Wchar <= 0x044F) {
        return wchar_t(std::uint16_t(_Wchar) - 0x0020);
    }
    if (_Wchar == 0x0451) {
        return wchar_t(0x0401);
    }

    return _Wchar;
}

bool Utf8toWStr(const std::string& _Utf8str, std::wstring& _Wstr);
bool Utf8toWStr(char const* _Utf8str, size_t _Csize, wchar_t* _Wstr, size_t& _Wsize);
inline bool Utf8toWStr(const std::string& _Utf8str, wchar_t* _Wstr, size_t& _Wsize) {
    return Utf8toWStr(_Utf8str.c_str(), _Utf8str.size(), _Wstr, _Wsize);
}
bool WStrToUtf8(wchar_t* _Wstr, size_t _Size, std::string& _Utf8str);

void HexEncodeByteArray(std::uint8_t* _Bytes, std::size_t _Size, std::string& _Result);
std::uint32_t GetUInt32ValueFromArray(const Tokens& _Data, std::uint16_t _Index);

#endif // SHARED_UTIL_H_
